package vsqlbuilder.gui;

import static vsqlbuilder.gui.diagram.DiagramModel.TABLE_IMAGE_ICON;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import vsqlbuilder.gui.diagram.DiagramModel;
import vsqlbuilder.gui.diagram.GTable;
import vsqlbuilder.logic.LogicModel;
import vsqlbuilder.logic.Schema;
import vsqlbuilder.logic.Table;

public class TablesTree extends JTree implements ActionListener, MouseListener {
	static final long serialVersionUID = 0L; //TODO [low]: temporary fix, must be
												// change to correct one
	
	private static final int tablesSeparation = 50;
	
	private LogicModel DatabaseLogic;
	private DiagramModel DatabaseModel;
	private JPopupMenu rightmenu;
	private JMenuItem rightmenuItem;
	protected DefaultMutableTreeNode selectedNode; // node selected on right
													// click
	protected DefaultMutableTreeNode topnode = null;
	
	protected int lastX=10, lastY=10, maxY=0;

	
	public TablesTree(MainController controller) {
		// register root node of tree & database's object that contains logic of
		// database
		super(new DefaultTreeModel(new DefaultMutableTreeNode("Tables [Double click add]")));
		this.setToolTipText("Right click or double click to add a table to the diagram (Query Statement)");
		topnode = (DefaultMutableTreeNode) this.getModel().getRoot();
		//TODO [low]: check this setEditable twice is right???
		this.setEditable(true);
		this.getSelectionModel().setSelectionMode(
				TreeSelectionModel.SINGLE_TREE_SELECTION);
		this.setShowsRootHandles(true);
		this.setEditable(false);
		//Set the icon for leaf nodes.
        if (TABLE_IMAGE_ICON != null) {
            DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
            renderer.setLeafIcon(TABLE_IMAGE_ICON);
            this.setCellRenderer(renderer);
        } else {
            System.out.println("Leaf icon missing; using default.");
        }

		DatabaseLogic = controller.Logic;
		DatabaseModel = controller.Model;
		// initialize & add right click context menu
		rightmenu = new JPopupMenu();
		this.add(rightmenu);

		rightmenuItem = new JMenuItem("Add Table to Query");
		rightmenuItem.setHorizontalTextPosition(JMenuItem.RIGHT);
		rightmenuItem.addActionListener(this);
		rightmenu.add(rightmenuItem);
		//rightmenu.add(this);
		this.addMouseListener(this);
	}

	private DefaultMutableTreeNode insertSchemaNode(Schema s) {
		DefaultMutableTreeNode schemanode;
		schemanode = new DefaultMutableTreeNode(s);
		topnode.add(schemanode);
		return schemanode;
	}

	private DefaultMutableTreeNode insertTableNode(
			DefaultMutableTreeNode parent, Table t) {
		DefaultMutableTreeNode tablenode;
		tablenode = new DefaultMutableTreeNode(t);
		parent.add(tablenode);
		return tablenode;
	}

	public void constructTree() {
		DefaultMutableTreeNode parentschema;
		for (Schema s : this.DatabaseLogic.schemas) {
			parentschema = insertSchemaNode(s);
			for (Table t : s.tables) {
				insertTableNode(parentschema, t);
			}
		}
		this.expandRow(0);
	}

	// Right Click context menu showed
	public void mousePressed(MouseEvent e) {
		if (e.isPopupTrigger()) {
			rightmenu.show(this, e.getX(), e.getY());
			TreePath selPath = this.getPathForLocation(e.getX(), e.getY());
			this.setSelectionPath(selPath);
			selectedNode = null;
			try {
				selectedNode = (DefaultMutableTreeNode) selPath
						.getLastPathComponent();
			} catch (Exception E) {
				System.out
						.println("Error selecting node of Jtree for right click action menu");
			}
		}
	}

	// Right Click context menu Actions to Perform
	public void actionPerformed(ActionEvent event) {
		Table t;
		if (event.getSource() == rightmenuItem && selectedNode != null) {
			Object nodeInfo = selectedNode.getUserObject();
			if ((nodeInfo instanceof Table) && (selectedNode.isLeaf())) {
				t = (Table) nodeInfo;
				if(!this.DatabaseModel.isUsedTable(t)){
					GTable drawTable=this.DatabaseModel.addTable(lastX, lastY, t);
					recalculateXY(drawTable.getWidth(),drawTable.getHeight());
				}
			}
		}
	}

	//TODO [low]: Optimize this, because actionPerformed & mouseClicked can be partially join in one function
	public void mouseClicked(MouseEvent e) {
		if(e.getClickCount()==2 && this.isEnabled()){ //if a double click is issue
			TreePath selPath = this.getPathForLocation(e.getX(), e.getY());
			if(selPath!=null){
				this.setSelectionPath(selPath);
				selectedNode = null;
				try {
					selectedNode = (DefaultMutableTreeNode) selPath.getLastPathComponent();
				} catch (Exception E) {
					System.out.println("Error selecting node of Jtree for right click action menu");
				}
				
				Table t;
				if (selectedNode != null) {
					Object nodeInfo = selectedNode.getUserObject();
					if ((nodeInfo instanceof Table) && (selectedNode.isLeaf())) {
						t = (Table) nodeInfo;
						if(!this.DatabaseModel.isUsedTable(t)){
							GTable drawTable=this.DatabaseModel.addTable(lastX, lastY, t);
							recalculateXY(drawTable.getWidth(),drawTable.getHeight());
						}
					}
				}
			}
		}
	}

	public void mouseReleased(MouseEvent e) {
		// Added for implements requirement can be used in the future or not
	}

	public void mouseEntered(MouseEvent e) {
		// Added for implements requirement can be used in the future or not
	}

	public void mouseExited(MouseEvent e) {
		// Added for implements requirement can be used in the future or not
	}

	public void emptyTree() {
		topnode.removeAllChildren();
		DefaultTreeModel model = (DefaultTreeModel) this.getModel();
		model.reload();
	}
	
	//TODO [HIGH]: This function only try to do a fixed organization of graphics objects
	//Should be implemented something better than this and must be locate in graphic library of project with their variables.
	public void recalculateXY(int width, int height){
		//calculate maxX & maxY
		maxY= Math.max(lastY+height,maxY);

		//if table position is larger than model then should be create a new row (move Y coordinate)
		if((lastX+tablesSeparation+width) > this.DatabaseModel.getModelWidth()-tablesSeparation){
			lastY=maxY+tablesSeparation;
			maxY=lastY;
			lastX=tablesSeparation;
		}else{ //if there is room for X coordinate then add width of table + space
			lastX+=width+tablesSeparation;
		}
	}
}
